package classification;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;

import weka.classifiers.Evaluation;
import weka.classifiers.bayes.NaiveBayes;
import weka.classifiers.functions.MultilayerPerceptron;
import weka.classifiers.lazy.IBk;
import weka.classifiers.meta.Bagging;
import weka.classifiers.meta.LogitBoost;
import weka.classifiers.trees.J48;
import weka.core.Instance;
import weka.core.Instances;
import weka.core.converters.ConverterUtils.DataSource;

/**
 * TODO: Your comment here
 * 
 * Usage:
 * 
 * Classifier c = Classifier.getInstance("data.arff"); ... ... (covert sound
 * file to weka.core.Instance) ... String class = c.classify(instance);
 * 
 */
public class Classifier {

	private static final ClassifierPriorities priorities = new ClassifierPriorities();
	private static final List<String> lowPriorityLetters = Arrays.asList("A",
			"E");
	private static volatile Classifier classifier = null;
	private static List<weka.classifiers.Classifier> classifiers = new ArrayList<weka.classifiers.Classifier>();
	private static Instances instances;

	public static Classifier getClassifier(Instances data) throws Exception {
		if (classifier == null) {
			synchronized (Classifier.class) {

				if (classifier == null) {

					String[] options = new String[2];

					setInstances(data);

					Evaluation eval = new Evaluation(data);

					classifier = new Classifier();
					
					

					IBk c1 = new IBk(5);
					c1.setCrossValidate(true);
					eval.crossValidateModel(c1, data, 10, new Random(1));
					System.out.println("c1 Percent correct: "
							+ Double.toString(eval.pctCorrect()));
					c1.buildClassifier(data);
					classifiers.add(c1);

					IBk c2 = new IBk(7);
					c2.setCrossValidate(true);
					eval.crossValidateModel(c2, data, 10, new Random(1));
					System.out.println("c2 Percent correct: "
							+ Double.toString(eval.pctCorrect()));
					c2.buildClassifier(data);
					classifiers.add(c2);

					J48 c3 = new J48();
					eval.crossValidateModel(c3, data, 5, new Random(1));
					System.out.println("c3 Percent correct: "
							+ Double.toString(eval.pctCorrect()));
					c3.buildClassifier(data);
					classifiers.add(c3);

					options[0] = "-N";
					options[1] = "100";
					MultilayerPerceptron c4 = new MultilayerPerceptron();
					c4.setOptions(options);
					eval.crossValidateModel(c4, data, 10, new Random(1));
					System.out.println("c4 Percent correct: "
							+ Double.toString(eval.pctCorrect()));
					c4.buildClassifier(data);
					classifiers.add(c4);
					classifiers.add(c4); // celowo, zwiększam wagę sieci
										//	 neuronowej

					IBk c5 = new IBk(1);
					c5.setCrossValidate(true);
					eval.crossValidateModel(c5, data, 10, new Random(1));
					System.out.println("c5 Percent correct: "
							+ Double.toString(eval.pctCorrect()));
					c5.buildClassifier(data);
					classifiers.add(c5);
					
					NaiveBayes c6 = new NaiveBayes();
					eval.crossValidateModel(c6, data, 10, new Random(1));
					c6.buildClassifier(data);
					System.out.println("c6 Percent correct: "
							+ Double.toString(eval.pctCorrect()));
					classifiers.add(c6);
					
					LogitBoost c7 = new LogitBoost();
					eval.crossValidateModel(c7, data, 10, new Random(1));
					c7.buildClassifier(data);
					System.out.println("c7 Percent correct: "
							+ Double.toString(eval.pctCorrect()));
					classifiers.add(c7);

				}
			}
		}
		return classifier;
	}

	private Classifier() {
	}

	public String classifyPair(Instance i1, Instance i2) throws Exception {
		String classify1 = classifier.classify(i1);
		System.out.println("Aaaaand it's " + classify1);
		String classify2 = classifier.classify(i2);
		System.out.println("...or it's " + classify2);

		if (lowPriorityLetters.contains(classify1)
				&& !lowPriorityLetters.contains(classify2)) {
			return classify2;
		}

		return classify1;
	}

	public String classify(Instance single) throws Exception {

		Map<String, Integer> rates = new HashMap<String, Integer>();
		int i = 0;
		for (weka.classifiers.Classifier c : classifiers) {

			double dClass = c.classifyInstance(single);
			String sAttr = single.dataset().classAttribute()
					.value((int) dClass);
			
			int weight = 1;
			if(priorities.getWeights().get(i).contains(sAttr)) {
				weight = 2;
			}
			
			System.out.println("Classifier " + i + " : " + sAttr + " w: " + weight);
			i++;
			
			
			if (rates.containsKey(sAttr)) {
				int current = rates.get(sAttr);
				rates.put(sAttr, current + weight);
			} else {
				rates.put(sAttr, weight);
			}

		}

		int max = 0;
		String bestClass = "ERROR";

		for (String clazz : rates.keySet()) {
		//	System.out.println("Class: " + clazz + ", support: "
		//			+ rates.get(clazz));
			if (rates.get(clazz) > max) {
				max = rates.get(clazz);
				bestClass = clazz;

			}
		}
		String[] classes = bestClass.split("");
		if (lowPriorityLetters.contains(classes[1])
				&& !lowPriorityLetters.contains(classes[classes.length - 1])) {
			return classes[classes.length - 1];
		}

		return classes[1];
	}

	public static Instances getInstances() {
		return instances;
	}

	public static void setInstances(Instances instances) {
		Classifier.instances = instances;
	}

}
